#include "config.h"

#include <windows.h>

#include "sr.h"
#include "ecmconf.h"

static BOOL
is_service_not_installed(const char *svcname)
{
	SC_HANDLE	schSCManager;
	SC_HANDLE	schService;

	schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (schSCManager == NULL)
		return FALSE;
	schService = OpenService(schSCManager, svcname, SERVICE_ALL_ACCESS);
	if (schService == NULL) {
		CloseServiceHandle(schSCManager);
		return ECM_TRUE;
	}
	CloseServiceHandle(schService);
	CloseServiceHandle(schSCManager);
	return ECM_FALSE;
}

ECMBOOL
is_service_started(const char *name, const char *svcname)
{
	SC_HANDLE	schSCManager;
	SC_HANDLE	schService;
	SERVICE_STATUS	SS;

	schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (schSCManager == NULL)
		return ECM_FALSE;
	schService = OpenService(schSCManager, svcname, SERVICE_ALL_ACCESS);
	if (schService == NULL) {
		CloseServiceHandle(schSCManager);
		return ECM_FALSE;
	}

	if (QueryServiceStatus(schService, &SS) != 0 && SS.dwCurrentState == SERVICE_RUNNING)
		return ECM_TRUE;
	return ECM_FALSE;
}

ECMBOOL
start_service(const char *name, const char *svcname, const char *args)
{
	SC_HANDLE	schSCManager;
	ECMBOOL		res = ECM_FALSE;

	schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (schSCManager) {
		SC_HANDLE	schService;

		schService = OpenService(schSCManager, svcname, SERVICE_ALL_ACCESS);
		if (schService) {
			if (StartService(schService, 0, NULL)) {
				SERVICE_STATUS	SS;
				while (QueryServiceStatus(schService, &SS)) {
					if (SS.dwCurrentState == SERVICE_RUNNING) {
						res = ECM_TRUE;
						break;
					}
					else if (SS.dwCurrentState == SERVICE_START_PENDING)
						Sleep(SS.dwWaitHint / 10);
					else
						break;
				}
			}
			CloseServiceHandle(schService);
		}
		CloseServiceHandle(schSCManager);
	}
	return res;
}

ECMBOOL
stop_service(const char *name, const char *svcname)
{
	SC_HANDLE	schSCManager;
	ECMBOOL		res = ECM_FALSE;

	schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (schSCManager) {
		SC_HANDLE	schService;

		schService = OpenService(schSCManager, svcname, SERVICE_ALL_ACCESS);
		if (schService) {
			SERVICE_STATUS	SS;

again:
			if (QueryServiceStatus(schService, &SS)) {
				while (SS.dwCurrentState != SERVICE_STOPPED) {
					if (!ControlService(schService, SERVICE_CONTROL_STOP, &SS)) {
						DWORD	Err;

						Err = GetLastError();
						switch (Err) {
						case ERROR_SERVICE_CANNOT_ACCEPT_CTRL:
						{
							Sleep(300);
							goto again;
						}				     
						case ERROR_SERVICE_NOT_ACTIVE:
							res = TRUE;
							goto out;
						default:
							goto out;
						}
					}
					if (SS.dwCurrentState == SERVICE_STOPPED)
						break;
					Sleep(SS.dwWaitHint);
					if (!QueryServiceStatus(schService, &SS)) {
						if (GetLastError() == ERROR_SERVICE_CANNOT_ACCEPT_CTRL) {
							Sleep(300);
							goto again;
						}
						goto out;
					}
				}
				res = ECM_TRUE;
			}
out:
			CloseServiceHandle(schService);
		}
		else
			res = ECM_TRUE;
		CloseServiceHandle(schSCManager);
	}
	return res;
}

ECMBOOL
install_service(const char *name, const char *svcname, const char *dispname, const char *dep, const char *args)
{
	SC_HANDLE	schService;
	SC_HANDLE	schSCManager;
	char	*command, *executable, *modname;
	ECMBOOL	res = ECM_FALSE;

	if (!is_service_not_installed(svcname))
		return ECM_TRUE;

	ecm_asprintf(&executable, "%s.exe", name);
	command = ecm_gen_path(ecm_default_clxdir(), "bin", executable, NULL);
	ecm_free(executable);

	if (args) {
		ecm_asprintf(&modname, "%s %s", command, args);
		ecm_free(command);
	}
	else
		modname = command;

	schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
	if (schSCManager) {
		schService = CreateService(schSCManager, svcname,
					dispname,
					SERVICE_ALL_ACCESS,
					SERVICE_WIN32_OWN_PROCESS,
					SERVICE_AUTO_START,
					SERVICE_ERROR_NORMAL,
					modname,
					NULL,
					NULL,
					dep,
					NULL,
					NULL);
		if (schService) {
			res = ECM_TRUE;
			CloseServiceHandle(schService);
		}
		CloseServiceHandle(schSCManager);
	}
	ecm_free(modname);
	return res;
}

ECMBOOL
remove_service(const char *name, const char *svcname)
{
	SC_HANDLE	schService;
	SC_HANDLE	schSCManager;
	ECMBOOL	res = ECM_FALSE;

	schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
	if (schSCManager) {
		schService = OpenService(schSCManager, svcname, DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
		if (schService == NULL)
			res = ECM_TRUE;
		else if (DeleteService(schService)) {
			res = ECM_TRUE;
			CloseServiceHandle(schService);
		}
		CloseServiceHandle(schSCManager);
	}
	return res;
}
